<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
  <script src="http://d3js.org/d3.v7.min.js"></script>
	
	<style>
		body{
      margin: 0;
    }
    .svgBox{
      background-color: #f0f0f0; 
      overflow: hidden; 
      height: 100vh; 
      width: 100vw; 
      margin: 0 auto;
    }
    #mainsvg{
      display: flex;
      justify-content: center;
      align-items: center;
      background: -webkit-linear-gradient(top, transparent 15px, #ccc 0),
          -webkit-linear-gradient(left, transparent 15px, #ccc 0);
        background-size: 16px 16px;
    }
	</style>
</head>
<body>
  <div class="svgBox">
    <svg id="mainsvg" class="svgs" height="100%" width="100%"></svg>
  </div>
</body>

<script>
	let nodes =  [
		{id: 1, name: 'Node 1'},
		{id: 2, name: 'Node 2'},
		{id: 3, name: 'Node 2'},
		{id: 4, name: 'Node 2'},
		{id: 5, name: 'Node 3'}
	]
	let edges =  [
		{source: 1, target: 2, relation: "100M", value: 1.3},
		{source: 3, target: 2, relation: "100M", value: 1.3},
		{source: 3, target: 4, relation: "100M", value: 1.3},
		{source: 3, target: 5, relation: "100M", value: 1.3},
		{source: 1, target: 3, relation: "10M", value: 1.3}
	]


function render() {


	let res = [
      {
        "root": true,
        "sn": "qwer",
        "mac": "a1",
        "id": 1,
        "nodes": []
      },
      {
        "root": false,
        "sn": "asd",
        "mac": "b1",
        "id": 2,
        "nodes": [
          {
            "mac": "a1",
            "port": 1,
            "self_port": 2,
            "is_blocked": false,
            "unknown": false
          }
        ]
      },
      {
        "root": false,
        "sn": "zxc",
        "mac": "c1",
        "id": 3,
        "nodes": [
          {
            "mac": "a1",
            "port": 2,
            "self_port": 3,
            "is_blocked": false,
            "unknown": false
          },
          {
            "mac": "b1",
            "port": 3,
            "self_port": 2,
            "is_blocked": true,
            "unknown": false
          },
          {
            "mac": "not found",
            "port": 3,
            "self_port": 1,
            "is_blocked": true,
            "unknown": true
          }
        ]
      },
      {
        "root": false,
        "sn": "asdd",
        "mac": "b2",
        "id": 4,
        "nodes": [
          {
            "mac": "a1",
            "port": 1,
            "self_port": 2,
            "is_blocked": false,
            "unknown": false
          },
          {
            "mac": "c1",
            "port": 1,
            "self_port": 1,
            "is_blocked": false,
            "unknown": false
          },
          {
            "mac": "d1",
            "port": 1,
            "self_port": 3,
            "is_blocked": false,
            "unknown": false
          },
          {
            "mac": "e1",
            "port": 1,
            "self_port": 4,
            "is_blocked": false,
            "unknown": false
          },
          {
            "mac": "f1",
            "port": 1,
            "self_port": 5,
            "is_blocked": false,
            "unknown": false
          },
          {
            "mac": "g1",
            "port": 1,
            "self_port": 6,
            "is_blocked": false,
            "unknown": false
          },
          {
            "mac": "h1",
            "port": 1,
            "self_port": 7,
            "is_blocked": false,
            "unknown": false
          },
        ]
      },
      {
        "root": false,
        "sn": "qwer",
        "mac": "c1",
        "id": 5,
        "nodes": []
      },
      {
        "root": false,
        "sn": "qwer",
        "mac": "d1",
        "id": 6,
        "nodes": []
      },
      {
        "root": false,
        "sn": "qwer",
        "mac": "e1",
        "id": 7,
        "nodes": []
      },
      {
        "root": false,
        "sn": "qwer",
        "mac": "f1",
        "id": 8,
        "nodes": []
      },
      {
        "root": false,
        "sn": "qwer",
        "mac": "g1",
        "id": 9,
        "nodes": []
      },
      {
        "root": false,
        "sn": "qwer",
        "mac": "h1",
        "id": 10,
        "nodes": []
      },
    ]


  let nodes = []
  let edges = []
  
  res.forEach(it => {
    nodes.push({
      id: it.id,
      mac: it.mac,
      label: it.mac,
      sn: it.sn,
      image: 'https://noc.ruijie.com.cn/macc5/img/GW.cc09734c.svg',
      level: nodes.length, //层级 
      font: {color: it.root ? 'red' : 'black', align: 'top'},
      root: it.root
    })

    if (it.nodes && it.nodes.length != 0) {
      it.nodes.forEach(item => {
        res.forEach(new_item => {
          if (new_item.mac == item.mac) {
            edges.push({
              source: new_item.id,
              target: it.id,
              mac: it.mac,
              label: item.port + '-' + item.self_port,
              color: item.is_blocked ? 'red' : 'green'
            })
          }
          if (item.unknown && item.port == new_item.id) {
            edges.push({
              source: new_item.id,
              target: item.mac,
              mac: it.mac,
              label: item.port + '-' + item.self_port,
              color: item.is_blocked ? 'red' : 'green',
              level: nodes.length, //层级 
            })
          }
        })
        
        if (item.unknown) {
          nodes.push({
            id: item.mac,
            mac: item.mac,
            label: item.mac,
            sn: 'xxx',
            image: 'https://noc.ruijie.com.cn/macc5/img/GW.cc09734c.svg',
            root: false
          })
        } 
      })
    }
  })
  
	
	// 移除
  d3.selectAll('#gGroup').remove()
  
  // 获取容器
  const svg = d3.select('#mainsvg')
  // 容器宽度
  const svgwidth = svg._groups[0][0].clientWidth
  // 容器高度
  const svgheight = svg._groups[0][0].clientHeight
  // 外边距
  const margin = {top: 100, right: 50, bottom: 100, left: 50}
  // 宽度
  const innerWidth = svgwidth - margin.left - margin.right
  // 高度
  const innerHeight = svgheight - margin.top - margin.bottom


  // 给svg容器添加一个组,用于存放其他的组件
  const g = svg.append('g')
  // 设置id
  .attr('id', 'gGroup')

  // 设置g放大缩小
  const zoom = d3.zoom()
  svg.call(zoom.on('zoom', (e) => {
    g.attr('transform', e.transform)
  }))
  // 移动到中间位置
  svg.transition()
  .duration(0) // 移动时间
  .call(
    zoom.transform, 
    d3.zoomIdentity.translate(svgwidth / 2, margin.top + 100).scale(.5)
  );


	

	//新建一个力导向图
	let forceSimulation = d3.forceSimulation()
		.force("link", d3.forceLink().id(d => d.id)) // 自定义连线source
		.force("charge", d3.forceManyBody())
		.force("center", d3.forceCenter())
    // .force('center', d3.forceCenter(0, svgheight / 2))
    // 碰撞力 防止节点重叠
    .force('collide',d3.forceCollide().radius(150).iterations(1))

	//生成节点数据
	forceSimulation.nodes(nodes)
		.on('tick', linksTick);//这个函数下面会讲解


	//生成边数据
	forceSimulation.force('link')
		.links(edges)
		.distance(function (d, i) {
			return 120;//设置边长
		});

	//设置图形 中心点
	forceSimulation.force('center')
		.y(svgheight / 5)//设置y坐标

	//再来看下顶点数据 和 边数据
	console.log(nodes, 11);
	console.log(edges, 11);


	//创建边线
	let links = g.selectAll('.link').data(edges)
    .join('g')
    .attr('class', 'link')
		.append('path')
		.attr('stroke', d => {
			return d.color
		})
		.attr('id', d => d.mac + d.index)
		.attr('class', 'myPath')
		.attr('stroke-width', '6') //设置边线宽度

  // 设置线条内容
  let linkTop = g.selectAll('.linkTop').data(edges)
    .join('text')
    .attr('class', '.linkTop')
    .attr('font-size', 24)
    .text(d => d.label)



	//创建节点
	let node = g.selectAll('.node').data(nodes)
		.join('g')
    .attr('class', 'node')
		.attr('transform', function (d, i) {
			return 'translate(' + d.x + ',' + d.y + ')'
		})
    .attr('r', 200)
		.call(
			drag()
		);


	//绘制矩形
	d3.selectAll('.node').data(nodes).append('rect')
    .attr('x', d => d.x - 85)
    .attr('y', d => d.y - 50)
    .attr('width', 170)
    .attr('height', 100)
    .attr('rx', 4)
    .attr('ry', 4)
    .attr('fill', '#fff')
		.append('text')
		.text(d => d.label + 5555555555)
		.attr('height', 40)
		.attr('width', 40)
		.style('color', '#000')
  
  
  // 添加图片
	d3.selectAll('.node').data(nodes).append('image')
    .attr('href', d => {
      console.log(d);
      if(d.id == 1){
        return 'https://noc.ruijie.com.cn/macc5/img/network.bbdf5e66.png'
      } else {
        return 'https://noc.ruijie.com.cn/macc5/img/GW.cc09734c.svg'
      }
    })
    .attr('x', d => {
      if ( d.root ) {
        return d.x - 45
      } else {
        return d.x - 68
      }
    })
    .attr('y', d => {
      if ( d.root ) {
        return d.y - 40
      } else {
        return d.y - 30
      }
    })
    .text('sfsdfs')
  
  // 在矩形中添加文字
	d3.selectAll('.node').data(nodes).append('text')
    .attr('text-anchor', 'middle')
    .attr('x', d => {
      if ( d.root ) {
        return d.x
      } else {
        return d.x
      }
    })
    .attr('y', d => {
      if ( d.root ) {
        return d.y
      } else {
        return d.y
      }
    })
    .text(d => d.root ? '' :  d.mac)
  

	//绘制文字
	// gs.append('text')
	// 	.text(d => {
	// 		return d.mac
	// 	})
	// 	.attr('x', d => {
	// 		return d.x - 30
  //   })
  //   .attr('y', d => {
	// 		return d.y + 15
  //   })






	function linksTick() {

    function elbow(d) {
      let sourceX = d.source.x,
        sourceY = d.source.y + 100, // 这个数字决定上面高度
        targetX = d.target.x,
        targetY = d.target.y;

      return "M" + sourceX + "," + sourceY +
        "V" + ((targetY - sourceY) / 4 + sourceY) +
        "H" + targetX +
        "V" + targetY;
    }

		links.attr('d', d => 'M' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y)

    // 线条内容
		linkTop
			.attr("x", function (d) {
				return (d.source.x + d.target.x) / 2;
			})
			.attr("y", function (d) {
				return (d.source.y + d.target.y) / 2;
			})

			node && node.attr('transform', function (d, i) {
				return 'translate(' + d.x + ',' + d.y + ')';
			})
	}

	function drag() {
		function dragstarted(event, d) {
			if (!event.active) forceSimulation.alphaTarget(0.3).restart();
			d.fx = d.x;
			d.fy = d.y;
		}
		function dragged(event, d) {
			d.fx = event.x;
			d.fy = event.y;
		}

		function dragended(event, d) {
			if (!event.active) forceSimulation.alphaTarget(0);
				d.fx = null;
				d.fy = null;
			}
			return d3.drag()
				.on("start",dragstarted)
				.on("drag",dragged)
				.on("end",dragended);
	}
}

render()

</script>
</html>